﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Transactions;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using Microsoft.Web.WebPages.OAuth;
using WebMatrix.WebData;
using BDShop.Filters;
using BDShop.Models;
using BDShop.Utilities;
using BDShop.Framework.Web;
using BDShop.Entities;
using BDShop.Business;
using BDShop.Framework.Security;
using BDShop.Business.Cache;
using PagedList;
using BDShop.Configuration;
using BDShop.Utilities.Managers;

namespace BDShop.Controllers
{
    [Authorize]
    [InitializeSimpleMembership]
    public class AccountController : Controller
    {
        //
        // GET: /Account/Login

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            LoginModel loginModel=new LoginModel();
            return View(loginModel);
        }

        //
        // GET: /Account/ForgotPassword

        [AllowAnonymous]
        public ActionResult ForgotPassword()
        {
            ForgotPasswordModel forgotPasswordModel=new ForgotPasswordModel();
            return View(forgotPasswordModel);
        }


        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ForgotPassword(ForgotPasswordModel model)
        {
            // Attempt to register the user

            //check user existance
            var user = Membership.GetUser(model.UserName);
            if (user == null)
            {
                TempData["Message"] = "Người dùng không tồn tại trong hệ thống.";
            }
            else
            {
                //generate password token
                var token = WebSecurity.GeneratePasswordResetToken(model.UserName);
                //create url with above token
                var resetLink ="<a href='"+Request.Url.GetLeftPart(UriPartial.Authority)+UrlManager.ResetPasswordlUrl(model.UserName,token)+"'>Cấp lại mật khẩu mới</a>";
                //get user emailid
                User userEntities = new User();
                userEntities = UserService.GetByUserName(model.UserName);
                var emailid = userEntities.Email;
                //send mail
                string subject = "Xác nhận để cấp lại mật khẩu mới";
                string body = "<b>Quý khách vui lòng click link sau để hệ thống cấp lại mật khẩu mới</b><br/>" + resetLink; //edit it
                try
                {
                    EmailComplex email=new EmailComplex();
                    email = EmailService.Init();
                    email.To = emailid;
                    email.Subject = subject;
                    email.Body = body;
                    EmailHelper.SendEMail(email);
                    TempData["Message"] = "Thông tin xác nhận đã gửi tới email của bạn. Vui lòng kiểm tra thông tin qua email.";
                }
                catch (Exception ex)
                {
                    TempData["Message"] = "Có lỗi trong quá trình gửi email.";
                }
            }
            return View(model);
        }

        [AllowAnonymous]
        public ActionResult ResetPassword(string un, string rt)
        {
            SEOModels seoModels=new SEOModels();

            UsersContext db = new UsersContext();
            //TODO: Check the un and rt matching and then perform following
            //get userid of received username
            var userid = UserService.GetByUserName(un).ID;
            //check userid and token matches
            webpages_Membership memberShip = MemberShipService.VerificationToken(userid, rt);
            bool any = (memberShip != null) ? true : false;

            if (any == true)
            {
                //generate random password
                string newpassword = SercurityHelper.GenerateRandomPassword(6);
                //reset password
                bool response = WebSecurity.ResetPassword(rt, newpassword);
                if (response == true)
                {
                    //get user emailid to send password
                    var emailid = UserService.GetByUserName(un).Email;
                    //send email
                    string subject = "Mật khẩu mới";
                    string body = "<b>Hệ thống đã cấp lại mật khẩu mới cho quý khách như bên dưới:</b><br/>" + newpassword; //edit it
                    try
                    {
                        EmailComplex email = new EmailComplex();
                        email = EmailService.Init();
                        email.To = emailid;
                        email.Subject = subject;
                        email.Body = body;
                        EmailHelper.SendEMail(email);
                        TempData["Message"] = "Đã gửi mật khẩu mới về email của quý khách.";
                    }
                    catch (Exception ex)
                    {
                        TempData["Message"] = "Có lỗi trong quá trình gửi email.";
                    }

                }
                else
                {
                    TempData["Message"] = "Có lỗi trong quá trình cấp mât khẩu mới.";
                }
            }
            else
            {
                TempData["Message"] = "Thông tin xác nhận không hợp lệ.";
            }

            return View(seoModels);
        }

        //
        // GET: /Account/Profile

        [HttpGet]
        public ActionResult Profile(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;

            ProfileModel profile = new ProfileModel();
            //Get user infomation
            User user = UserService.GetByUserName(WebSecurity.CurrentUserName);
            profile.fullname = user.FullName;
            profile.mobile = user.Phone;
            profile.address = user.Address;
            profile.gender = user.Gender;
            profile.birthday = user.Birthday.ToString("dd/MM/yyyy");
            profile.email = user.Email;
            return View(profile);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Profile(ProfileModel model)
        {
            User userByPhone = UserService.GetByPhone(model.mobile);
            if (userByPhone != null)
            {
                if (userByPhone.ID != WebSecurity.CurrentUserId)
                {
                    ModelState.AddModelError("", "Số điện thoại đăng ký bị trùng.");
                    return View(model);
                }
            }

            User user = new User();
            user.ID = WebSecurity.CurrentUserId;
            user.UserName = WebSecurity.CurrentUserName;
            user.FullName = model.fullname;
            user.Address = model.address;
            user.Phone = model.mobile;
            user.Email = model.email;
            user.Gender = model.gender;
            user.Birthday = Util.ConvertStringToDate(model.birthday);

            if (UserService.Update(user))
            {
                ModelState.AddModelError("", "Cập nhật thông tin tài khoản thành công.");
            }
            else
            {
                // If we got this far, something failed, redisplay form
                ModelState.AddModelError("", "Có lỗi trong quá trình cập nhật thông tin tài khoản.");
            }
            
            return View(model);
        }

        //
        // GET: /Account/OrderHistory

        public ActionResult OrderHistory(int? page = 1)
        {
            SEOModels seoModels = new SEOModels();
            ViewBag.siteName = seoModels.siteName;
            ViewBag.htmlDescription = seoModels.htmlDescription;
            ViewBag.htmlKeywords = seoModels.htmlKeywords;

            int pageSize = BDShopConfiguration.Web.pageSize;

            //Get order from session
            List<Order> lstOrder = OrderService.GetByUser(WebSecurity.CurrentUserId);

            if (lstOrder == null)
            {
                lstOrder = new List<Order>();
            }

            //Paging
            int pageNumber = (page ?? 1);
            return View(lstOrder.ToPagedList(pageNumber, pageSize));
        }

        //
        // GET: /Account/OrderDetail

        public ActionResult OrderDetail(string id)
        {
            SEOModels seoModels = new SEOModels();
            ViewBag.siteName = seoModels.siteName;
            ViewBag.htmlDescription = seoModels.htmlDescription;
            ViewBag.htmlKeywords = seoModels.htmlKeywords;

            long orderID;
            try
            {
                orderID = long.Parse(SercurityHelper.Decrypt(SercurityHelper.ConvertHexToString(id), true));
            }
            catch (Exception)
            {
                return RedirectToAction("OrderHistory");
            }

            //Get order from session
            List<OrderDetail> lstOrderDetail = OrderDetailService.GetByOrderID(orderID);

            if (lstOrderDetail == null)
            {
                return RedirectToAction("Index", "Home");
            }
            ViewBag.OrderDetail = lstOrderDetail;

            OrderModels order = new OrderModels();
            //Get user infomation
            User user = UserService.GetByUserName(WebSecurity.CurrentUserName);
            order.fullName = user.FullName;
            order.gender = user.Gender;

            //get order
            Order orderResult = OrderService.GetByID(orderID);
            order.city = orderResult.City;
            order.district = orderResult.District;
            order.paymentMethod = orderResult.PaymentMethod;
            order.shippingCost = orderResult.ShippingCost;
            order.total = orderResult.Total;
            order.mobile = orderResult.Phone;
            order.address = orderResult.Address;

            return View(order);

        }

        //
        // GET: /Account/WishList

        public ActionResult WishList(int? page = 1)
        {
            SEOModels seoModels=new SEOModels();
            ViewBag.siteName = seoModels.siteName;
            ViewBag.htmlDescription = seoModels.htmlDescription;
            ViewBag.htmlKeywords = seoModels.htmlKeywords;

            int pageSize = BDShopConfiguration.Web.pageSize;

            List<WishList> lstWist = WishListService.GetByUserID(WebSecurity.CurrentUserId);

            //Paging
            int pageNumber = (page ?? 1);
            return View(lstWist.ToPagedList(pageNumber, pageSize));
        }

        [HttpPost]
        public ActionResult RemoveProductOfWishList(string ID)
        {
            try
            {
                long wlID;

                wlID = long.Parse(SercurityHelper.Decrypt(SercurityHelper.ConvertHexToString(ID), true));

                WishList wl = WishListService.GetByID(wlID);
                if (WishListService.Remove(wl))
                {

                    return Json(new { success = true });
                }
                else
                {
                    return Json(new { errors = true });
                }

            }
            catch (Exception)
            {
                return Json(new { errors = true });
            }
        }

        //
        // GET: /Account/ChangePassword
        [HttpGet]
        public ActionResult ChangePassword()
        {
            ChangePasswordModel changePasswordModel=new ChangePasswordModel();
            return View(changePasswordModel);
        }

        //
        // POST: /Account/ChangePassword
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            if (WebSecurity.ChangePassword(WebSecurity.CurrentUserName, model.OldPassword, model.Password))
            {
                ModelState.AddModelError("", "Đổi mật khẩu thành công.");
            }
            else
            {
                ModelState.AddModelError("", "Đổi mật khẩu không thành công.");
            }

            return View();
        }

        //
        // POST: /Account/Login

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Login(LoginModel model, string returnUrl)
        {
            if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
            {
                return RedirectToLocal(returnUrl);
            }

            // If we got this far, something failed, redisplay form
            ModelState.AddModelError("", "Tên đăng nhập hoặc mật khẩu không đúng.");
            return View(model);
        }

        [HttpGet]
        public ActionResult LogOff()
        {
            return View();
        }

        //
        // POST: /Account/LogOff

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff(FormCollection frm)
        {
            WebSecurity.Logout();

            return RedirectToAction("Index", "Home");
        }

        //
        // GET: /Account/Register

        [HttpGet]
        [AllowAnonymous]
        public ActionResult Register()
        {
            RegisterModel registerModel=new RegisterModel();
            return View(registerModel);
        }

        //
        // POST: /Account/Register

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            // Attempt to register the user
            try
            {
                if (UserService.GetByUserName(model.UserName) != null)
                {
                    ModelState.AddModelError("", "Tên đăng nhập bị trùng.");
                }
                else
                {
                    if (UserService.GetByEmail(model.Email) != null)
                    {
                        ModelState.AddModelError("", "Email đăng ký bị trùng.");
                    }
                    else
                    {
                        if (UserService.GetByPhone(model.mobile) != null)
                        {
                            ModelState.AddModelError("", "Số điện thoại đăng ký bị trùng.");
                        }
                        else
                        {
                            var roles = (SimpleRoleProvider)Roles.Provider;
                            var membership = (SimpleMembershipProvider)Membership.Provider;

                            RoleProvider role = new SimpleRoleProvider();

                            if (!roles.RoleExists("Customer"))
                            {
                                roles.CreateRole("Customer");
                            }
                            if (membership.GetUser(model.UserName, false) == null)
                            {
                                WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
                                                                 new
                                                                     {
                                                                         FullName = model.FullName,
                                                                         Gender = model.Gender,
                                                                         Phone = model.mobile,
                                                                         Address = model.Address,
                                                                         Email = model.Email,
                                                                         Birthday =
                                                                     Util.ConvertStringToDate(model.birthday)
                                                                     });
                            }
                            if (!roles.GetRolesForUser(model.UserName).Contains("Customer"))
                            {
                                roles.AddUsersToRoles(new[] { model.UserName }, new[] { "Customer" });
                            }

                            WebSecurity.Login(model.UserName, model.Password);
                            return RedirectToAction("Index", "Home");
                        }



                    }
                }
            }
            catch (MembershipCreateUserException e)
            {
                ModelState.AddModelError("", "Có lỗi trong quá trình đăng ký tài khoản");
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // POST: /Account/Disassociate

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Disassociate(string provider, string providerUserId)
        {
            string ownerAccount = OAuthWebSecurity.GetUserName(provider, providerUserId);
            ManageMessageId? message = null;

            // Only disassociate the account if the currently logged in user is the owner
            if (ownerAccount == User.Identity.Name)
            {
                // Use a transaction to prevent the user from deleting their last login credential
                using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Serializable }))
                {
                    bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
                    if (hasLocalAccount || OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name).Count > 1)
                    {
                        OAuthWebSecurity.DeleteAccount(provider, providerUserId);
                        scope.Complete();
                        message = ManageMessageId.RemoveLoginSuccess;
                    }
                }
            }

            return RedirectToAction("Manage", new { Message = message });
        }

        //
        // GET: /Account/Manage

        public ActionResult Manage(ManageMessageId? message)
        {
            ViewBag.StatusMessage =
                message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
                : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
                : message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
                : "";
            ViewBag.HasLocalPassword = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
            ViewBag.ReturnUrl = Url.Action("Manage");
            return View();
        }

        //
        // POST: /Account/Manage

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Manage(LocalPasswordModel model)
        {
            bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
            ViewBag.HasLocalPassword = hasLocalAccount;
            ViewBag.ReturnUrl = Url.Action("Manage");
            if (hasLocalAccount)
            {
                if (ModelState.IsValid)
                {
                    // ChangePassword will throw an exception rather than return false in certain failure scenarios.
                    bool changePasswordSucceeded;
                    try
                    {
                        changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
                    }
                    catch (Exception)
                    {
                        changePasswordSucceeded = false;
                    }

                    if (changePasswordSucceeded)
                    {
                        return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
                    }
                    else
                    {
                        ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
                    }
                }
            }
            else
            {
                // User does not have a local password so remove any validation errors caused by a missing
                // OldPassword field
                ModelState state = ModelState["OldPassword"];
                if (state != null)
                {
                    state.Errors.Clear();
                }

                if (ModelState.IsValid)
                {
                    try
                    {
                        WebSecurity.CreateAccount(User.Identity.Name, model.NewPassword);
                        return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess });
                    }
                    catch (Exception e)
                    {
                        ModelState.AddModelError("", e);
                    }
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        //
        // POST: /Account/ExternalLogin

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        }

        //
        // GET: /Account/ExternalLoginCallback

        [AllowAnonymous]
        public ActionResult ExternalLoginCallback(string returnUrl)
        {
            AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
            if (!result.IsSuccessful)
            {
                return RedirectToAction("ExternalLoginFailure");
            }

            if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
            {
                return RedirectToLocal(returnUrl);
            }

            if (User.Identity.IsAuthenticated)
            {
                // If the current user is logged in add the new account
                OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // User is new, ask for their desired membership name
                string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
                ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
                ViewBag.ReturnUrl = returnUrl;
                return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });
            }
        }

        //
        // POST: /Account/ExternalLoginConfirmation

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl)
        {
            string provider = null;
            string providerUserId = null;

            if (User.Identity.IsAuthenticated || !OAuthWebSecurity.TryDeserializeProviderUserId(model.ExternalLoginData, out provider, out providerUserId))
            {
                return RedirectToAction("Manage");
            }

            if (ModelState.IsValid)
            {
                // Insert a new user into the database
                using (UsersContext db = new UsersContext())
                {
                    UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == model.UserName.ToLower());
                    // Check if user already exists
                    if (user == null)
                    {
                        // Insert name into the profile table
                        db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
                        db.SaveChanges();

                        OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
                        OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

                        return RedirectToLocal(returnUrl);
                    }
                    else
                    {
                        ModelState.AddModelError("UserName", "User name already exists. Please enter a different user name.");
                    }
                }
            }

            ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(provider).DisplayName;
            ViewBag.ReturnUrl = returnUrl;
            return View(model);
        }

        //
        // GET: /Account/ExternalLoginFailure

        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

        [AllowAnonymous]
        [ChildActionOnly]
        public ActionResult ExternalLoginsList(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return PartialView("_ExternalLoginsListPartial", OAuthWebSecurity.RegisteredClientData);
        }

        [ChildActionOnly]
        public ActionResult RemoveExternalLogins()
        {
            ICollection<OAuthAccount> accounts = OAuthWebSecurity.GetAccountsFromUserName(User.Identity.Name);
            List<ExternalLogin> externalLogins = new List<ExternalLogin>();
            foreach (OAuthAccount account in accounts)
            {
                AuthenticationClientData clientData = OAuthWebSecurity.GetOAuthClientData(account.Provider);

                externalLogins.Add(new ExternalLogin
                {
                    Provider = account.Provider,
                    ProviderDisplayName = clientData.DisplayName,
                    ProviderUserId = account.ProviderUserId,
                });
            }

            ViewBag.ShowRemoveButton = externalLogins.Count > 1 || OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
            return PartialView("_RemoveExternalLoginsPartial", externalLogins);
        }

        #region Helpers
        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

        public enum ManageMessageId
        {
            ChangePasswordSuccess,
            SetPasswordSuccess,
            RemoveLoginSuccess,
        }

        internal class ExternalLoginResult : ActionResult
        {
            public ExternalLoginResult(string provider, string returnUrl)
            {
                Provider = provider;
                ReturnUrl = returnUrl;
            }

            public string Provider { get; private set; }
            public string ReturnUrl { get; private set; }

            public override void ExecuteResult(ControllerContext context)
            {
                OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
            }
        }

        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
        #endregion
    }
}
